import javax.xml.crypto.Data;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;

public class UdpEchoServer {
      // 创建DataGramSocket 对象, 用来后面操作网卡
    private DatagramSocket socket = null ;
     //通过构造方法来初始化 socket ,
    public UdpEchoServer(int port) throws SocketException {
         // 服务器程序要自己指定号好口号 , port指定一个端口号
        this.socket = new DatagramSocket(port);
        // 而客户端就不需要特殊指定, 由系统分配
        this.socket = new DatagramSocket();   // 这样就是由系统自动分配端口号
    }

    // 用start 启动服务器 ;
    public  void start() throws IOException {
        System.out.println("服务器启动");
         while (true){       // 服务器是 一直运行的, 所以while(true) 保证服务器运行 ;
             // 1. 读取请求并解析 ; 从网卡读到数据并解析 ;
             // 用receive 读数据
             DatagramPacket requestPacket = new DatagramPacket(new byte[4096] , 4096) ;
             //因为DatagramPacket 对象读取到的数据需要放到一个内存空间里,
             // 但DatagramPacket内部不能自行分配空间,所有要手动把空间创建好给它,这个byte数组就是这样来到
             socket.receive(requestPacket) ; // DatagramPacket 对象用来承载 从网卡中读到的数据 ;
              // 服务器一起动,执行到receive方法,读取数据,如果客户端的数据还没有请求,
             // 那么receive就会阻塞,  直到客户端数据过来 ;

             // 当完成receive操作后, 数据就以 二进制 的形式存储到 DatagramPacket里了 ;
             // 要把这些数据取出来用, 还要把二进制数据 转出字符串 ;
             String request = new String(requestPacket.getData() , 0 , requestPacket.getLength()) ;
             //  getData() 把数据存的数组取出来 , 然后就是去数组里 0 到 length区间 的数据取出 ;
             //  length是receive读取到的数据的长度 , 不是 我们创建的数组的4096 ;

             //2. 根据请求计算响应 (一般服务器都会有的过程) 服务器最核心的步骤

             // 因为当前是 echo Server 不涉及这些 计算, 所以不需要考虑怎么计算 , 只要有请求过来,就把请求当做相应 ;
             // 回显服务器, 请求是什么样的,响应就是什么样的
             String response = process(request);

             // 3. 把响应结果 写回到客户端 ;
             // 创建一个 DatagramPacket对象, 把数据给 DatagramPacket对象,
             // 然后通过 send 返回
                                                              // 这两个参数表示响应的数据是什么 ;
             DatagramPacket responsePacket = new DatagramPacket(response.getBytes() , response.getBytes().length ,
                     // 这里给一个参数, 表示这个数据是发给谁的 , getSocketAddress()方法返回是发动人的地址,
                     // 谁发送的请求 , 就发回给谁去
                     requestPacket.getSocketAddress()) ;

             // UDP是无连接的, 所以每次send 都要重新指定一下,这个数据报是发给谁的
             socket.send(responsePacket);

             // 4. 打印日志, 把这次交互的数据打印出来
             System.out.printf("[%s, %d] request= %s , response = %s\n" , requestPacket.getAddress().toString() ,
                     requestPacket.getPort() , request ,  response);
         }

    }
      // 计算响应过程 ;
    // 一般来说应该正常的服务器,计算响应过程是很重要,很复杂的 ;
    private String process(String request) {
        return request ;
    }

    public static void main(String[] args) throws IOException {
        UdpEchoServer udpEchoServerk = new UdpEchoServer(5050);
        udpEchoServerk.start();
    }
}
